package com.crazymakercircle.iodemo.fileDemos;

import com.crazymakercircle.NioDemoConfig;
import com.crazymakercircle.util.IOUtil;
import com.crazymakercircle.util.Logger;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.StandardOpenOption;

/**
 * Created by 尼恩@ 疯创客圈
 * JDK NIO复制文件
 * FileChannel inChannel = null;
 * FileChannel outchannel = null;
 * <p>
 * 通过传统IO流的获取流对应的通道
 * fis = new FileInputStream(srcFile);
 * fos = new FileOutputStream(destFile);
 * inChannel = fis.getChannel();
 * outchannel = fos.getChannel();
 */

public class FileNIOCopyDemo {

    /**
     * 演示程序的入口函数
     *
     * @param args
     */
    public static void main(String[] args) {
        //演示复制资源文件
        nioCopyResouceFile();

    }


    /**
     * 复制两个资源目录下的文件
     */
    public static void nioCopyResouceFile() {
        String sourcePath = NioDemoConfig.FILE_RESOURCE_SRC_PATH;
        String srcPath = IOUtil.getResourcePath(sourcePath);
        Logger.debug("srcPath=" + srcPath);

        String destShortPath = NioDemoConfig.FILE_RESOURCE_DEST_PATH;
        String destdePath = IOUtil.builderResourcePath(destShortPath);
        Logger.debug("destdePath=" + destdePath);

        //nioCopyFile(srcPath, destdePath);
        nioCopyFile2(srcPath, destdePath);
    }


    /**
     * IO流复制毫秒数：1467
     * 复制文件　，400多M的ZIP文件,比IO复制还花时间,NIO并不一定快啊
     * ByteBuffer buf = ByteBuffer.allocate(1024);  3824 ms
     * ByteBuffer buf = ByteBuffer.allocate(10240); 3010 ms
     * ByteBuffer buf = ByteBuffer.allocate(204800); 2819ms
     * ByteBuffer buf = ByteBuffer.allocate(20480);  2920ms
     * 不管怎么调整ByteBuf的缓冲区大小,还是比IO复制慢啊
     * <p>
     * nioCopyResouceFile |>  srcPath=/E:/sky_code_gitee2/netty_redis_zookeeper_source_code/NioDemos/target/classes/软件.zip
     * nioCopyResouceFile |>  destdePath=/E:/sky_code_gitee2/netty_redis_zookeeper_source_code/NioDemos/target/classes//软件2.zip
     * nioCopyFile |>  base 复制毫秒数：3824
     * <p>
     * 测试时间硬件不同 结果不同 但是总的排名应该是： 直接缓冲区 463ms > 普通io 1467ms > 非直接缓冲区 2684 ms
     * 下面这个代码是非直接缓冲区
     * <p>
     * 直接缓冲区和非直接缓冲区的区别?
     * 直接缓冲区比非直接缓冲区在运行效率上要高一些，是什么原因造成这样的结果呢？直接缓冲区是使用 DirectByteBuffer 类进行实现的，而非直接缓冲区是使用 HeapByteBuffer 类进行实现的 。
     * 直接缓冲区（ DirectByteBuffer）在内部使用 sun.misc.Unsafe 类进行值的处理。 Unsafe类的作用是 JVM 与操作系统进行直接通信，提高程序运行的效率，但正如其类的名称Unsafe 一样，该类在使用上并不是安全的，如果程序员使用不当，那么极有可能出现处理数据上的错误，因此，该类并没有公开化（ public ），仅由 JDK 内部使用 。
     * 非直接缓冲区（ HeapByteBuffer）在内部直接对 byte口 hb 字节数组进行操作，而且还是在 JVM 的堆中进行数据处理，因此运行效率相对慢一些 。
     *
     * @param srcPath
     * @param destPath
     */
    public static void nioCopyFile(String srcPath, String destPath) {

        File srcFile = new File(srcPath);
        File destFile = new File(destPath);

        try {
            //如果目标文件不存在，则新建
            if (!destFile.exists()) {
                destFile.createNewFile();
            }


            long startTime = System.currentTimeMillis();

            FileInputStream fis = null;
            FileOutputStream fos = null;
            FileChannel inChannel = null;
            FileChannel outchannel = null;
            try {
                fis = new FileInputStream(srcFile);
                fos = new FileOutputStream(destFile);
                inChannel = fis.getChannel();
                outchannel = fos.getChannel();

                int length = -1;
                ByteBuffer buf = ByteBuffer.allocate(20480);
                //从输入通道读取到buf
                while ((length = inChannel.read(buf)) != -1) {

                    //翻转buf,变成成读模式
                    buf.flip();

                    int outlength = 0;
                    //将buf写入到输出的通道
                    while ((outlength = outchannel.write(buf)) != 0) {
                        //System.out.println("写入字节数：" + outlength);
                    }
                    //清除buf,变成写入模式
                    buf.clear();
                }


                //强制刷新磁盘
                outchannel.force(true);
            } finally {
                IOUtil.closeQuietly(outchannel);
                IOUtil.closeQuietly(fos);
                IOUtil.closeQuietly(inChannel);
                IOUtil.closeQuietly(fis);
            }
            long endTime = System.currentTimeMillis();
            Logger.debug("base 复制毫秒数：" + (endTime - startTime));

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 直接缓冲区
     * Paths.get(destPath) java.nio.file.InvalidPathException: Trailing char < > at index 86: E:/sky_code_gitee2/netty_redis_zookeeper_source_code/NioDemos/target/classes/软件.zip
     * Paths.get(destPath) 有中文报错,只能转成英文,不是有中文,而是路径properties路径后面有中文,我去
     * <p>
     * nioCopyResouceFile |>  srcPath=/E:/sky_code_gitee2/netty_redis_zookeeper_source_code/NioDemos/target/classes/软件.zip
     * nioCopyResouceFile |>  destdePath=/E:/sky_code_gitee2/netty_redis_zookeeper_source_code/NioDemos/target/classes//软件2.zip
     * nioCopyFile2 |>  base 复制毫秒数：463
     */
    public static void nioCopyFile2(String srcPath, String destPath) {
        // /E:/sky_code_gitee2/netty_redis_zookeeper_source_code/NioDemos/target/classes/软件.zip ,多了一个斜杠
        srcPath = srcPath.substring(1);

        File file = new File(srcPath);
        File file1 = new File(destPath);
        try {
            //如果目标文件不存在，则新建
            if (!file1.exists()) {
                file1.createNewFile();
            }
            long startTime = System.currentTimeMillis();
            FileChannel inChannel = null;
            FileChannel outchannel = null;
            try {

                inChannel = FileChannel.open(file.toPath(), StandardOpenOption.READ);
                outchannel = FileChannel.open(file1.toPath(), StandardOpenOption.WRITE, StandardOpenOption.READ, StandardOpenOption.CREATE);
                //内存映射文件
                MappedByteBuffer in = inChannel.map(FileChannel.MapMode.READ_ONLY, 0, inChannel.size());
                MappedByteBuffer out = outchannel.map(FileChannel.MapMode.READ_WRITE, 0, inChannel.size());

                //直接对缓冲区进行读写操作
                byte[] b = new byte[in.limit()];
                in.get(b);
                out.put(b);

                //上面三行可以换成一行
                //out.put(in);
            } finally {
                IOUtil.closeQuietly(inChannel);
                IOUtil.closeQuietly(outchannel);
            }
            long endTime = System.currentTimeMillis();
            Logger.debug("base 复制毫秒数：" + (endTime - startTime));

        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
